package cn.liontalk.controller;

import cn.liontalk.annotation.AccessLimit;
import cn.liontalk.common.redis.RedisService;
import cn.liontalk.common.redis.prefix.GoodsKey;
import cn.liontalk.common.redis.prefix.MiaoShaKey;
import cn.liontalk.common.result.CodeMsg;
import cn.liontalk.common.result.Result;
import cn.liontalk.entity.MiaoshaMessage;
import cn.liontalk.entity.OrderInfo;
import cn.liontalk.entity.User;
import cn.liontalk.rabbitmq.MQsender;
import cn.liontalk.service.GoodsService;
import cn.liontalk.service.SecKillService;
import cn.liontalk.vo.GoodsVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;


import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: 周哲
 * @package: cn.liontalk.demo
 * @description:
 * @date: 2019/1/18 14:35
 * @version: V1.0
 */
@Controller
@RequestMapping(value = "/sec")
public class SecKillController implements InitializingBean {

    private static Logger log = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private SecKillService secKillService;

    @Autowired
    private GoodsService goodsService;

    @Autowired
    private RedisService redisService;

    @Autowired
    MQsender mQsender;

    /**
     * 秒杀结束标志
     */
    private Map<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();


    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVO> list = goodsService.listGoodsVO();
        if (null != list) {
            for (GoodsVO goods : list) {
                long goodsId = goods.getId();
                redisService.set(GoodsKey.goodsId, "" + goodsId, goods.getStock());
                localOverMap.put(goodsId, false);
            }
        }
    }


    /**
     * 25000并发
     * QPS 85
     *
     * @param goodsId
     * @param user
     * @param model
     * @return
     */
    @RequestMapping(value = "/kill")
    public String goodsKill(@RequestParam("goodsId") Long goodsId, User user, Model model) {
        if (null == user) {
            return "login";
        }
        GoodsVO goodsVO = goodsService.queryGoodsById(goodsId);
        int stockCount = goodsVO.getStock();
        if (stockCount <= 0) {
            model.addAttribute("errmsg", CodeMsg.STOCK_NOT_ENOUGH.toString());
            return "good_kill_fail";
        }
        OrderInfo orderInfo = secKillService.queryGoodsKillInfoByGoodsIdAndUserId(goodsId, user.getId());
        if (null != orderInfo) {
            model.addAttribute("errmsg", CodeMsg.REPEAT_KILL.toString());
            return "good_kill_fail";
        }
        OrderInfo order = secKillService.secKillGoods(user, goodsVO);
        model.addAttribute("orderInfo", order);
        model.addAttribute("goods", goodsVO);
        return "order_detail";
    }


    /**
     * 利用前后端分离来进行秒杀
     *
     * @param goodsId 商品id
     * @param user    秒杀用户
     * @return OrderInfo 订单信息
     */
    @AccessLimit(needLogin = true,maxCount = 4,seconds=10)
    @ResponseBody
    @RequestMapping(value = "/detach/kill/{goodsId}")
    public Result<OrderInfo> getSecKillOrderInfo(@PathVariable("goodsId") Long goodsId, User user) {
        log.info("<<<<<<<<<<<<<<<<<< =====================detach kill=============================>>>>>>>>>>>>>");
        if (null == user) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        GoodsVO goodsVO = goodsService.queryGoodsById(goodsId);
        int stockCount = goodsVO.getStock();
        if (stockCount <= 0) {
            return Result.error(CodeMsg.STOCK_NOT_ENOUGH);
        }
        OrderInfo orderInfo = secKillService.queryGoodsKillInfoByGoodsIdAndUserId(goodsId, user.getId());
        if (null != orderInfo) {
            return Result.error(CodeMsg.REPEAT_KILL);
        }
        OrderInfo order = secKillService.secKillGoods(user, goodsVO);
        return Result.success(order);
    }


    /**
     * 利用消息队列来进行秒杀
     *
     * @param goodsId 商品id
     * @param user    秒杀用户
     * @return 秒杀成功的结果
     */
    @RequestMapping(value = "/mq/{path}/kill")
    @ResponseBody
    public Result<Integer> mqGoodsKill(@RequestParam("goodsId") Long goodsId, User user, @PathVariable("path") String path) {
        System.out.println("start sec kill  ....");
        if (null == user) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        //验证path
        boolean check = secKillService.checkPath(user, goodsId, path);
        if (!check) {
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }


        long stock = redisService.decr(GoodsKey.goodsId, "" + goodsId);
        if (stock < 0) {
            log.info("stock is :" + stock) ;
            localOverMap.put(goodsId, true);
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }
        OrderInfo orderInfo = secKillService.queryGoodsKillInfoByGoodsIdAndUserId(goodsId, user.getId());
        if (null != orderInfo) {
            return Result.error(CodeMsg.REPEAT_KILL);
        }

        MiaoshaMessage miaoshaMessage = new MiaoshaMessage();
        miaoshaMessage.setGoodId(goodsId);
        miaoshaMessage.setUser(user);
        mQsender.sendMiaoShaMessage(miaoshaMessage);
        log.info("<<<<<<<<<<<<<<<<<< ======================END  kill=============================>>>>>>>>>>>>>");
        return Result.success(0);
    }


    /**
     * 客户端轮询接口 判断是否秒杀到
     * <p>
     * orderID:成功  -1：秒杀失败  0：排队中
     *
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/kill/result", method = RequestMethod.GET)
    @ResponseBody
    public Result<Long> miaoshaResult(User user, @RequestParam("goodsId") long goodsId) {
        log.info("enter sec kill result ....");
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        long rst = secKillService.getMiaoshaResult(user.getId(), goodsId);
        return Result.success(rst);
    }


    @RequestMapping(value = "/kill/path", method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getMiaoShaPath(@RequestParam("goodsId") long goodsId, User user, @RequestParam("code") Long verifyCode) {
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        if (verifyCode == null) {
            return Result.error(CodeMsg.NEED_INPUT_VERIFY_CODE);
        }
        OrderInfo orderInfo = secKillService.queryGoodsKillInfoByGoodsIdAndUserId(goodsId, user.getId());
        if (null != orderInfo) {
            return Result.error(CodeMsg.REPEAT_KILL);
        }
        boolean checkCodeIsRight = secKillService.checkCodeIsRight(user, goodsId, verifyCode);
        if (!checkCodeIsRight) {
            return Result.error(CodeMsg.VERIFY_CODE_ERROR);
        }

        boolean check = secKillService.checkVerifyCode(user, goodsId, verifyCode);
        if (!check) {
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }
        String path = secKillService.createMiaoshaPath(user, goodsId);
        return Result.success(path);
    }


    /**
     * 获取秒杀时候的验证码
     *
     * @param response 返回数据
     * @param goodsId  商品id
     * @param user     用户信息
     * @return 秒杀商品的验证图片
     */
    @RequestMapping(value = "/kill/verifyCode")
    @ResponseBody
    public Result<String> getMiaoShaVerifyCode(HttpServletResponse response, @RequestParam("goodsId") long goodsId, User user) {
        if (null == user) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        try {
            BufferedImage image = secKillService.createVerifyCode(user, goodsId);
            OutputStream out = response.getOutputStream();
            ImageIO.write(image, "JPEG", out);
            out.flush();
            out.close();
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(CodeMsg.MIAOSHA_FAIL);
        }
    }
}
